package com.caipos.actor.service;

import com.caipos.actor.ActorSystemContext;
import com.caipos.actor.common.actors.KbActorCtx;
import com.caipos.actor.common.actors.KbActorException;
import com.caipos.actor.common.data.id.EntityId;
import com.caipos.actor.common.data.id.TenantId;
import com.caipos.actor.common.msg.KbActorStopReason;
import com.caipos.actor.shared.ComponentMsgProcessor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ContextAwareActor {

    private long lastPersistedErrorTs = 0L;
    protected final TenantId tenantId;
    protected final T id;
    protected P processor;
    private long messagesProcessed;
    private long errorsOccurred;

    public ComponentActor(ActorSystemContext systemContext, TenantId tenantId, T id) {
        super(systemContext);
        this.tenantId = tenantId;
        this.id = id;
    }

    abstract protected P createProcessor(KbActorCtx ctx);

    @Override
    public void init(KbActorCtx ctx) throws KbActorException {
        super.init(ctx);
        this.processor = createProcessor(ctx);
        initProcessor(ctx);
    }

    protected void initProcessor(KbActorCtx ctx) throws KbActorException {
        try {
            log.debug("[{}][{}][{}] Starting processor.", tenantId, id, id.getEntityType());
            processor.start(ctx);
            if (systemContext.isStatisticsEnabled()) {
                scheduleStatsPersistTick();
            }
        } catch (Exception e) {
            log.debug("[{}][{}] Failed to start {} processor.", tenantId, id, id.getEntityType(), e);
            logAndPersist("OnStart", e, true);
            throw new KbActorException("Failed to init actor", e);
        }
    }

    private void scheduleStatsPersistTick() {
    }

    @Override
    public void destroy(KbActorStopReason stopReason, Throwable cause) {
        try {
            log.debug("[{}][{}][{}] Stopping processor.", tenantId, id, id.getEntityType());
            if (processor != null) {
                processor.stop(ctx);
            }
        } catch (Exception e) {
            log.warn("[{}][{}] Failed to stop {} processor: {}", tenantId, id, id.getEntityType(), e.getMessage());
            logAndPersist("OnStop", e, true);
        }
    }



    protected void logAndPersist(String method, Exception e) {
        logAndPersist(method, e, false);
    }

    private void logAndPersist(String method, Exception e, boolean critical) {
        errorsOccurred++;
        String componentName = processor != null ? processor.getComponentName() : "Unknown";
        if (critical) {
            log.debug("[{}][{}][{}] Failed to process method: {}", id, tenantId, componentName, method);
            log.debug("Critical Error: ", e);
        } else {
            log.trace("[{}][{}][{}] Failed to process method: {}", id, tenantId, componentName, method);
            log.trace("Debug Error: ", e);
        }
    }


    protected abstract long getErrorPersistFrequency();

}
